package edu.usc.epigenome.dnaase.script;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.util.CloseableIterator;

import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;

import edu.usc.epigenome.genomeLibs.PicardUtils;
import edu.usc.epigenome.genomeLibs.MethylDb.MethylDbQuerier;

public class SamToPhase {
	/**
	 * @param args
	 */
	private static final String C_USAGE = "Use: SamToPhase -sample ctcf -windowSize 24 -GCH inputBam_file gff_file";
	public static String connStr = null;
	protected static Connection cConn = null;
	//public static String connStr = "jdbc:mysql://epifire2.epigenome.usc.edu/gnome_seq";
	protected int minOppoACount = 1;
    protected double minOppoAFreq = 0.10;
	//mysql_db_server: epifire2.epigenome.usc.edu
	
    @Option(name="-tablePrefix",usage="Prefix for DB table (default " + MethylDbQuerier.DEFAULT_METHYL_TABLE_PREFIX + ")")
    protected String tablePrefix = "methylCGsRich_gnome_seq_merge_s5_s6_";
    @Option(name="-HCG",usage=" just withdarw HCpG sites")
    protected boolean Hcg = false;
    @Option(name="-GCH",usage=" just withdarw HCpG sites")
    protected boolean Gch = false;
    @Option(name="-sample",usage=" input the sample name: ctcf")
    protected String sample = "ctcf";
    @Option(name="-windowSize",usage=" input the window size: default is 24bp")
    protected int windowSize = 20;
    @Option(name="-rangeSize",usage=" input the range size: default is 1000bp")
    protected int rangeSize = 1000;
    @Option(name="-alignmentType",usage="alignment type(1: 5' end; 2: 3' end; 3: center) the default is 3")
    protected int alignmentType = 3;
    @Option(name="-smooth",usage="smooth the plot, average data in 20 bp window, then mover 1 bp each step")
    protected boolean smooth = false;
    
    @Option(name="-minMapQ",usage="minimum mapping quality (default 30)")
	protected int minMapQ = 30;
	@Option(name="-minReadCov",usage="minimum read coverage (default 1)")
	protected int minReadCov = 1;
	@Option(name="-minCTReadCov",usage="minimum CT read coverage (default 3)")
	protected int minCTReadCov = 3;
	@Option(name="-minBaseQual",usage="minimum Base quality (default 0)")
	protected int minBaseQual = 0;
	@Option(name="-minOppositeGCount",usage="minimum G Allele Count (default 3)")
	protected static double minOppositeGCount = 3;
	@Option(name="-minOppositeACount",usage="minimum A Allele Count (default 1)")
	protected static double minOppositeACount = 1;	
	@Option(name="-minOppositeAFreq",usage="minimum BAllele Frequency (default 0.1)")
	protected static double minOppositeAFreq = 0.10;
	@Option(name="-minCTContent",usage="minCT read in the C site (default 0.9)")
	protected static double minCTContent = 0.90;
	@Option(name="-minGuanContent",usage="minGuan read in the HCG,GCH,GCG sites' G site (default 0.9)")
	protected static double minGuanContent = 0.90;
	@Option(name="-minHContent",usage="minGuan read in the HCG,GCH,GCG sites' H site (default 0.9)")
	protected static double minHContent = 0.90;

    
	// receives other command line parameters than options
	@Argument
	private List<String> arguments = new ArrayList<String>();
	
	public static void main(String[] args) 
	throws Exception{
		// TODO Auto-generated method stub
		new SamToPhase().doMain(args);
	}

	public void doMain(String[] args)
	throws Exception
	{
		CmdLineParser parser = new CmdLineParser(this);
		// if you have a wider console, you could increase the value;
		// here 80 is also the default
		
		parser.setUsageWidth(80);
		try
		{
			parser.parseArgument(args);

			if(arguments.size() < 2 ) {
				System.err.println(C_USAGE);
				System.exit(1);
			}
			String gffFileName = arguments.get(0);
			String bamFileName = arguments.get(1);
			BufferedReader br = new BufferedReader(new FileReader(gffFileName));
			String line;
			
			File inputSamOrBamFile = new File(bamFileName);
			SAMFileReader inputSam = new SAMFileReader(inputSamOrBamFile);
			inputSam.setValidationStringency(SAMFileReader.ValidationStringency.SILENT);
			
			String fn1 = this.tablePrefix + sample;
			String fn2 = this.tablePrefix + sample;
			if(smooth){
				if(Gch){
					fn1 += "_gch_table.smooth.txt";
					fn2 += "_gch_table.addtional.smooth.txt";
				}
				else if(Hcg){
					fn1 += "_hcg_table.smooth.txt";
					fn2 += "_hcg_table.addtional.smooth.txt";
				}
				else{
					fn1 += "_table.smooth.txt";
					fn2 += "_table.addtional.smooth.txt";
				}
			}
			else{
				if(Gch){
					fn1 += "_gch_table.txt";
					fn2 += "_gch_table.addtional.txt";
				}
				else if(Hcg){
					fn1 += "_hcg_table.txt";
					fn2 += "_hcg_table.addtional.txt";
				}
				else{
					fn1 += "_table.txt";
					fn2 += "_table.addtional.txt";
				}
			}
			
			PrintWriter outWriter = new PrintWriter(new File(fn1));
			//additional file to store c reads, t reads number detail
			PrintWriter outWriter2 = new PrintWriter(new File(fn2));

			

			int recCounter = 1;
			while( (line = br.readLine()) != null){
				if(!line.startsWith("chr"))
					continue;
				String[] tmpArray = line.split("\t");
				String chr = tmpArray[0];
				int chrStart = Integer.parseInt(tmpArray[3]); 
				int chrEnd = Integer.parseInt(tmpArray[4]);
				boolean negStrand = tmpArray[6].charAt(0) == '-' ? true : false;
				int motifCenter = 0;
				if(negStrand){
					if(alignmentType == 3){
						motifCenter = (chrEnd + chrStart)/2;
					}
					else if(alignmentType == 2){
						motifCenter = chrStart;
					}
					else if(alignmentType ==1){
						motifCenter = chrEnd;
					}
					else{
						System.err.println("alignment type error!");
					}
				}
				else{
					if(alignmentType == 3){
						motifCenter = (chrEnd + chrStart)/2;
					}
					else if(alignmentType == 2){
						motifCenter = chrEnd;
					}
					else if(alignmentType ==1){
						motifCenter = chrStart;
					}
					else{
						System.err.println("alignment type error!");
					}
				}
				
				int rangeStart = motifCenter - rangeSize;
				int rangeEnd = motifCenter + rangeSize;
				if(rangeStart < 0 || rangeEnd < 0 || rangeStart >= rangeEnd){
					System.err.println("motifCenter < 0");
				}
				
				
				
				//if ((recCounter % 100)==0)
				//{
				//	System.err.printf("On new record #%d\n",recCounter);
				//	System.gc();
				//}
				if ((recCounter % 100)==0)
				{
					System.err.printf("On new record #%d\n",recCounter);
					System.gc();
				}
				//System.err.println("motifCenter: " + motifCenter + " negStrand: " + negStrand);
				recCounter++;
				if(negStrand){
					if(smooth){
						for(int windowStart = rangeEnd - windowSize/2, windowEnd = rangeEnd + windowSize/2, i = 0; windowStart >= rangeStart - windowSize/2; windowStart--, windowEnd--, i++){
							try {
								//step 4: create a statement
								String queryString = getSql(tablePrefix,chr,windowStart,windowEnd);
								Statement stmt = cConn.createStatement();
								ResultSet queryResult = stmt.executeQuery(queryString);
								queryResult.next();
								
								double methyValue = queryResult.getDouble(1);
								int numC = queryResult.getInt(2);
								double cReads = queryResult.getDouble(3);
								double tReads = queryResult.getDouble(4);

								if(numC != 0){

									outWriter.printf("%.2f\t", methyValue);
									outWriter2.printf("%d\t%.2f\t%.2f\t", numC, cReads, tReads);

								}
								else{
									outWriter.printf("NA\t");
									outWriter2.printf("NA\tNA\tNA\t");
								}
								queryResult.close();
								
							}catch(SQLException ex) {
											System.err.println("Query: " + ex.getMessage());
							}
						}
					}
					else{
						for(int windowStart = rangeEnd - windowSize, windowEnd = rangeEnd, i = 0; windowStart >= rangeStart; windowStart -= windowSize, windowEnd -= windowSize, i++){
							try {
								//step 4: create a statement
								String queryString = getSql(tablePrefix,chr,windowStart,windowEnd);
								Statement stmt = cConn.createStatement();
								ResultSet queryResult = stmt.executeQuery(queryString);
								queryResult.next();
								
								double methyValue = queryResult.getDouble(1);
								int numC = queryResult.getInt(2);
								double cReads = queryResult.getDouble(3);
								double tReads = queryResult.getDouble(4);

								if(numC != 0){

									outWriter.printf("%.2f\t", methyValue);
									outWriter2.printf("%d\t%.2f\t%.2f\t", numC, cReads, tReads);

								}
								else{
									outWriter.printf("NA\t");
									outWriter2.printf("NA\tNA\tNA\t");
								}
								queryResult.close();
								
							}catch(SQLException ex) {
											System.err.println("Query: " + ex.getMessage());
							}
						}
					}
					
				}
				else{
					if(smooth){
						for(int windowStart = rangeStart - windowSize/2 , windowEnd = rangeStart + windowSize/2, i = 0; windowEnd <= rangeEnd + windowSize/2; windowStart++, windowEnd++, i++){
							try {
								//step 4: create a statement
								String queryString = getSql(tablePrefix,chr,windowStart,windowEnd);
								Statement stmt = cConn.createStatement();
								ResultSet queryResult = stmt.executeQuery(queryString);
								queryResult.next();
								
								double methyValue = queryResult.getDouble(1);
								
								
								int numC = queryResult.getInt(2);
								double cReads = queryResult.getDouble(3);
								double tReads = queryResult.getDouble(4);

								if(numC != 0){

									outWriter.printf("%.2f\t", methyValue);
									outWriter2.printf("%d\t%.2f\t%.2f\t", numC, cReads, tReads);

								}
								else{
									outWriter.printf("NA\t");
									outWriter2.printf("NA\tNA\tNA\t");
								}
								queryResult.close();
								
							}catch(SQLException ex) {
											System.err.println("Query: " + ex.getMessage());
							}
						}
					}
					else{
						for(int windowStart = rangeStart, windowEnd = rangeStart + windowSize, i = 0; windowEnd <= rangeEnd; windowStart+=windowSize, windowEnd+=windowSize, i++){
							try {
								//step 4: create a statement
								String queryString = getSql(tablePrefix,chr,windowStart,windowEnd);
								Statement stmt = cConn.createStatement();
								ResultSet queryResult = stmt.executeQuery(queryString);
								queryResult.next();
								
								double methyValue = queryResult.getDouble(1);
								
								
								int numC = queryResult.getInt(2);
								double cReads = queryResult.getDouble(3);
								double tReads = queryResult.getDouble(4);

								if(numC != 0){

									outWriter.printf("%.2f\t", methyValue);
									outWriter2.printf("%d\t%.2f\t%.2f\t", numC, cReads, tReads);

								}
								else{
									outWriter.printf("NA\t");
									outWriter2.printf("NA\tNA\tNA\t");
								}
								queryResult.close();
								
							}catch(SQLException ex) {
											System.err.println("Query: " + ex.getMessage());
							}
						}
					}
					
				}
				
				outWriter.printf("\n");
				outWriter2.printf("\n");

				
			}
			
			outWriter.close();
			outWriter2.close();		

		}
		catch (CmdLineException e)
		{
			System.err.println(e.getMessage());
			System.err.println(C_USAGE);
			// print the list of available options
			parser.printUsage(System.err);
			System.err.println();
			return;
		}	
	}
	
	protected double[] getMethyValue(String chr, int chrStart, int chrEnd, boolean negStrandLine, SAMFileReader inputSam){
		double[] result = new double[3];
		
		HashMap<Integer,Integer[]> hcgMap = new HashMap<Integer,Integer[]>();
		HashMap<Integer,Integer[]> gchMap = new HashMap<Integer,Integer[]>();
		
		boolean cytosineFlag = false;
		boolean cytosineNegFlag = false;
		boolean cytosinePassFlag = false;

		int totalNum = 0;
		
		int totalNumPos = 0;

		int totalNumNeg = 0;

		int totalNumC = 0;
		int totalNumT = 0;

		int totalNumAPos = 0;
		int totalNumANeg = 0;
		
		int totalNumGPos = 0;
		int totalNumGNeg = 0;
		
		
		CloseableIterator<SAMRecord> chrIt = inputSam.queryOverlapping(chr, chrStart, chrEnd);
		record: while (chrIt.hasNext())
		{
			SAMRecord samRecord = chrIt.next();
			int mapQual = samRecord.getMappingQuality();
			byte[] baseQual = samRecord.getBaseQualities();
			boolean unmapped = samRecord.getReadUnmappedFlag();
			if (unmapped || (mapQual < minMapQ))
			{
				continue record;
			}
			String seq = PicardUtils.getReadString(samRecord, true);
			try{
				
				String ref = PicardUtils.refStr(samRecord, true);
				if (seq.length() != ref.length())
				{
					System.err.println("SeqLen(" + seq.length() + ") != RefLen(" + ref.length() + ")");
					System.err.println(seq + "\n" + ref);
				}
				int seqLen = Math.min(seq.length(), ref.length());
				boolean negStrand = samRecord.getReadNegativeStrandFlag();
				if(negStrand != negStrandLine)
					continue;
				int alignmentS = samRecord.getAlignmentStart();
				//int readsStart = (negStrand) ? 0 - samRecord.getUnclippedEnd() : alignmentS;
				//int readsEnd = (negStrand) ? 0 - alignmentS : samRecord.getUnclippedEnd();
				int	onRefCoord = (negStrand) ? samRecord.getUnclippedEnd() : alignmentS; 
				for( int i = chrStart - alignmentS; i <= chrEnd - alignmentS; i++){
					if(i < 0 || i >= seqLen)
						continue;
					byte baseQS = baseQual[i];
					if( baseQS < minBaseQual )
						continue;
					if (ref.charAt(i) == '-')
						break record;
					
				}
				
				
				
				
				totalNum++;
				if(negStrand){
					totalNumNeg++;
			//		refNegRecord = ref.charAt(i);
					if(seq.charAt(i) == 'A')
						totalNumANeg++;
					if(seq.charAt(i) == 'G')
						totalNumGNeg++;
				//	if(i < seqLen-1)
					//	totalNumNegnext++;
				}
				else{
					totalNumPos++;
					//refPosRecord = ref.charAt(i);
					if(seq.charAt(i) == 'A')
						totalNumAPos++;
					if(seq.charAt(i) == 'G')
						totalNumGPos++;
				//	if(i < seqLen-1)
					//	totalNumPosnext++;
				}

				if(PicardUtils.isCytosine(i, seq, true)){
					cytosineFlag=true;
					if(PicardUtils.isCytosine(i, seq, false)){
						totalNumC++;
					}
					else{
						totalNumT++;
					}
					if(negStrand){
						cytosineNegFlag = true;
				//		if(i < seqLen-1){
					//		if(seq.charAt(i+1) == 'G'){
						//		totalNumGnext++;
							//}
							//else{
							//	totalNumHnext++;
							//}
								
					//	}
					}
					else{
						cytosineNegFlag = false;
						//if(i < seqLen-1){
							//if(seq.charAt(i+1) == 'G'){
								//totalNumGnext++;
							//}
							//else{
							//	totalNumHnext++;
							//}
						//}
					}
				}
				/*
				if(snpPosition == 8195215){
					if(negStrand){
						if(i > 0){
							System.err.println("neg ref.charAt(i): " + ref.charAt(i));
							System.err.println("neg ref.charAt(i+1): " + ref.charAt(i+1));
							System.err.println("-----------------------------------------");
						}
						
					}
					else{
						if(i < seqLen-1){
							System.err.println("ref.charAt(i): " + ref.charAt(i));
							System.err.println("ref.charAt(i+1): " + ref.charAt(i+1));
							System.err.println("-----------------------------------------");
						}
						
					}
					
				}*/
				
			}
			catch (Exception e)
			{
				System.err.println("-----------------------------------------");
				System.err.println("Couldn't handle seq #");
				System.err.println(seq);
				e.printStackTrace(System.err);
				System.err.println("-----------------------------------------");
//				chrIt.close();
//				System.exit(1);
			}
		}
		chrIt.close();
		return result;
		
	}
	
	
}


